package cn.kivensoft.util;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.function.Predicate;

/** 基于java8的实用函数集合
 * @author Kiven Lee
 * @version 1.2.0
 */
public final class Langs {
	// 一天的毫秒数
	public static final long MS_OF_DAY = 86400_000;
	// 本地时区偏移值, 毫秒为单位
	public static final long LOCAL_ZONE_OFFSET = TimeZone.getDefault().getRawOffset();

	public static final ThreadLocal<Calendar> calThreadLocal = ThreadLocal.withInitial(Calendar::getInstance);

	/** 比较两个对象是否相等
	 * @param src 源对象
	 * @param dst 目标对象
	 * @return 相等返回true，不等返回false
	 */
	public static <T> boolean isEquals(T src, T dst) {
		return Objects.equals(src, dst);
	}

	/** 校验变量是否为null
	 * @param args 要判断的目标对象
	 * @return 全部为null返回true, 否则返回false
	 */
	public static boolean isNull(Object... args) {
		for (Object arg : args)
			if (arg != null)
				return false;
		return true;
	}

	/** 校验变量是否不为null
	 * @param args 要校验的多个元素
	 * @return 全部不为null返回true, 否则返回false
	 */
	public static boolean isNotNull(Object... args) {
		for (Object arg : args)
			if (arg == null)
				return false;
		return true;
	}

	/** 校验变量是否为空(包括null和没有元素), 可判断类型: String/Array/Collection/Map/Iterable
	 * @param arg 要检查的变量
	 * @return 是否为空
	 */
	public static boolean isEmpty(Object arg) {
		if (arg == null) return true;
		Class<?> cls = arg.getClass();
		if (cls == String.class) return ((String)arg).isEmpty();
		if (cls == ArrayList.class) return ((ArrayList<?>)arg).isEmpty();
		if (cls == HashMap.class) return ((HashMap<?, ?>)arg).isEmpty();
		if (cls.isArray()) return Array.getLength(arg) == 0;
		if (arg instanceof Collection<?>) return ((Collection<?>)arg).isEmpty();
		if (arg instanceof Map<?, ?>) return ((Map<?, ?>)arg).isEmpty();
		if (arg instanceof Iterable<?>) return !((Iterable<?>)arg).iterator().hasNext();
		if (arg instanceof CharSequence) return ((CharSequence)arg).length() == 0;
		return false;
	}

	/** 校验多个变量是否为空(包括null和没有元素), 可判断类型: String/Array/Collection/Map/Iterable
	 * @param args 多个变量
	 * @return 任意一个不为空则返回false, 否则返回true
	 */
	public static boolean isEmpty(Object... args) {
		for (Object arg : args)
			if (!isEmpty(arg))
				return false;
		return true;
	}

	public static boolean isNotEmpty(Object arg) {
		return !isEmpty(arg);
	}

	/** 校验多个变量是否不为空(包括null和没有元素), 可判断类型: String/Array/Collection/Map/Iterable
	 * @param args 多个变量
	 * @return 任意一个为空则返回false, 否则返回true
	 */
	public static boolean isNotEmpty(Object... args) {
		for (Object arg : args)
			if (isEmpty(arg))
				return false;
		return true;
	}
	
	/** 获取异常的堆栈详细内容
	 * @param e 异常变量
	 * @return 堆栈详细内容
	 */
	public static String getStackTrace(Exception e) {
		StringWriter sw = new StringWriter();
		try {
			e.printStackTrace(new PrintWriter(sw));
		} catch(Exception ignored) {}
		return sw.toString();
	}

	/** 将null值转为def
	 * @param value 传入值
	 * @param def 缺省值
	 * @return value==null返回def, 否则返回value
	 */
	public static <T> T mapNull(T value, T def) {
		return value == null ? def : value;
	}

	/** 生成map字典
	 * @param args key1, value1, key2, value2, ... 形式的参数
	 * @return 字典对象
	 */
	@SuppressWarnings("unchecked")
	public static <K, V> HashMap<K, V> mapOf(Object... args) {
		HashMap<K, V> ret = new HashMap<>(args.length);
		for (int i = 0, imax = args.length - 1; i < imax; i += 2)
			ret.put((K) args[i], (V) args[i + 1]);
		return ret;
	}

	/** 生成列表
	 * @param args 列表项
	 * @return 列表
	 */
	@SafeVarargs
	public static <T> ArrayList<T> listOf(T... args) {
		ArrayList<T> ret = new ArrayList<>(args.length);
		Collections.addAll(ret, args);
		return ret;
	}

	/** 生成指定大小的泛型数组
	 * @param elementClass 数组项的类
	 * @param size 数组大小
	 * @return 数组
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newArray(Class<?> elementClass, int size) {
		return (T) Array.newInstance(elementClass, size);
	}

	/** 生成数组
	 * @param args 数组项
	 * @return 数组
	 */
	@SafeVarargs
	public static <T> T[] arrayOf(T... args) {
		return args;
	}

	/** 拷贝字典表中的指定键值
	 * @param dst 目标字典
	 * @param src 源字典
	 * @param args 要复制的键名，多个
	 */
	@SafeVarargs
	public static <T1, T2> void transTo(Map<T1, T2> dst, Map<T1, T2> src, T1... args) {
		if (args.length == 0)
			dst.putAll(src);
		else
			for (T1 arg : args)
				dst.put(arg, src.get(arg));
	}

    /** 返回第一个回调为true的元素在数组中的索引位置
     * @param array 数组
     * @param predicate 回调函数
     * @return 找到返回指定索引, 否则返回-1
     */
    public static <T> int indexOf(T[] array, Predicate<T> predicate) {
		if (array != null)
			for (int i = 0, imax = array.length; i < imax; ++i)
				if (predicate.test(array[i]))
					return i;
		return -1;
    }

	/** 返回第一个回调为true的元素在数组中的索引位置
	 * @param source 集合
	 * @param predicate 回调函数
	 * @return 找到返回指定索引, 否则返回-1
	 */
	public static <T> int indexOf(Collection<T> source, Predicate<T> predicate) {
		if (source != null) {
			int idx = 0;
			for (T item : source) {
				if (predicate.test(item))
					return idx;
				++idx;
			}
		}
		return -1;
	}

	/** 查找元素
	 * @param array 数组
	 * @param predicate 回调函数
	 * @return 找到返回该元素, 否则返回null
	 */
	public static <T> T find(T[] array, Predicate<T> predicate) {
		if (array != null)
			for (T item : array)
				if (predicate.test(item))
					return item;
		return null;
	}

	/** 查找元素
     * @param source 可迭代的对象
     * @param predicate 回调函数
     * @return 找到返回该元素, 否则返回null
     */
    public static <T> T find(Collection<T> source, Predicate<T> predicate) {
		if (source != null)
			for (T v : source)
				if (predicate.test(v))
					return v;
		return null;
    }

	/** 获取系统的今天，只包含日期部分
	 * @return 只包含日期的Date
	 */
	public static Date today() {
		return new Date((System.currentTimeMillis() + LOCAL_ZONE_OFFSET)
				/ MS_OF_DAY * MS_OF_DAY - LOCAL_ZONE_OFFSET);
	}

	/** 增加年份
	 * @param date 源日期
	 * @param years, 可为负数
	 * @return 增加指定年份的新Date
	 */
	public static Date addYears(Date date, int years) {
		Calendar cal = calThreadLocal.get();
		cal.setTime(date);
		if (years != 0) cal.add(Calendar.YEAR, years);
		return cal.getTime();
	}

	/** 增加月份
	 * @param date 源日期
	 * @param months, 可为负数
	 * @return 增加指定月份的新Date
	 */
	public static Date addMonths(Date date, int months) {
		Calendar cal = calThreadLocal.get();
		cal.setTime(date);
		if (months != 0) cal.add(Calendar.MONTH, months);
		return cal.getTime();
	}

	/** 增加天数
	 * @param date 源日期
	 * @param days, 天数, 可为负数
	 * @return 增加指定天数的新Date
	 */
	public static Date addDays(Date date, int days) {
		return new Date(date.getTime() + MS_OF_DAY * days);
	}

	/** 增加年月日
	 * @param date 源日期
	 * @param years 年份, 可为负数
	 * @param months 月份, 可为负数
	 * @param days 日期, 可为负数
	 * @return 增加指定时间的新Date
	 */
	public static Date addDate(Date date, int years, int months, int days) {
		Calendar cal = calThreadLocal.get();
		cal.setTime(date);
		if (years != 0) cal.add(Calendar.YEAR, years);
		if (months != 0) cal.add(Calendar.MONTH, months);
		if (days != 0) cal.add(Calendar.DAY_OF_MONTH, days);
		return cal.getTime();
	}

	/** 增加年月日时分秒
	 * @param date 源日期
	 * @param years 年份, 可为负数
	 * @param months 月份, 可为负数
	 * @param days 日期, 可为负数
	 * @param hours 小时, 可为负数
	 * @param minutes 分钟, 可为负数
	 * @param seconds 秒, 可为负数
	 * @return 新的Date变量
	 */
	public static Date addDate(Date date, int years, int months, int days, int hours, int minutes, int seconds) {
		Calendar cal = calThreadLocal.get();
		cal.setTime(date);
		if (years != 0) cal.add(Calendar.YEAR, years);
		if (months != 0) cal.add(Calendar.MONTH, months);
		if (days != 0) cal.add(Calendar.DAY_OF_MONTH, days);
		if (hours != 0) cal.add(Calendar.HOUR, hours);
		if (minutes != 0) cal.add(Calendar.MINUTE, minutes);
		if (seconds != 0) cal.add(Calendar.SECOND, seconds);
		return cal.getTime();
	}

	/** 增加时分秒
	 * @param date 源日期
	 * @param hours 小时, 可为负数
	 * @param minutes 分钟, 可为负数
	 * @param seconds 秒, 可为负数
	 * @return 新的Date变量
	 */
	public static Date addTime(Date date, int hours, int minutes, int seconds) {
		return new Date(date.getTime() + hours * 3600_000L + minutes * 60_000L + seconds * 1000L);
	}

	/** 返回一个新的日期变量，值为日期参数的日期部分
	 * @param date 源日期
	 * @return 新的Date变量
	 */
	public static Date onlyDate(Date date) {
		return new Date((date.getTime() + LOCAL_ZONE_OFFSET) / MS_OF_DAY * MS_OF_DAY - LOCAL_ZONE_OFFSET);
	}

	/** GMT转换为本地时间
	 * @param date 源日期
	 * @return 新的Date变量
	 */
	public static Date fromGmt(Date date) {
		return new Date(date.getTime() + LOCAL_ZONE_OFFSET);
	}

	/** 合并两个int成long
	 * @param high 高位int
	 * @param low 低位int
	 * @return 合并后的long
	 */
	public static long mergeInt(int high, int low) {
		return ((long)high << 32) | ((long)low & 0xFFFFFFFFL);
	}

	/** 将long分割为两个int, 写入int[]数组
	 * @param src 源值
	 * @param dst 写入目标数组
	 * @param pos 写入起始位置
	 */
	public static void unmergeInt(long src, int[] dst, int pos) {
		dst[pos] = (int)(src >> 32 & 0xFFFFFFFFL);
		dst[pos + 1] = (int)(src &0xFFFFFFFFL);
	}

	/** 获取long的高位int
	 * @param value 原始值
	 * @return long的高位值
	 */
	public static int highInt(long value) {
		return (int)(value >> 32 & 0xFFFFFFFFL);
	}

	/** 获取long的低位int
	 * @param value 原始值
	 * @return long的低位值
	 */
	public static int lowInt(long value) {
		return (int)(value &0xFFFFFFFFL);
	}


	@FunctionalInterface
	public interface Act {
		void accept();
	}

	@FunctionalInterface
	public interface Act1<T1> {
		void accept(T1 arg);
	}

	@FunctionalInterface
	public interface Act2<T1, T2> {
		void accept(T1 arg1, T2 arg2);
	}

	@FunctionalInterface
	public interface Act3<T1, T2, T3> {
		void accept(T1 arg1, T2 arg2, T3 arg3);
	}

	@FunctionalInterface
	public interface Act4<T1, T2, T3, T4> {
		void accept(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
	}

	@FunctionalInterface
	public interface Func<R> {
		R apply();
	}

	@FunctionalInterface
	public interface Func1<T1, R> {
		R apply(T1 arg);
	}

	@FunctionalInterface
	public interface Func2<T1, T2, R> {
		R apply(T1 arg1, T2 arg2);
	}

	@FunctionalInterface
	public interface Func3<T1, T2, T3, R> {
		R apply(T1 arg1, T2 arg2, T3 arg3);
	}

	@FunctionalInterface
	public interface Func4<T1, T2, T3, T4, R> {
		R apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
	}

}
